package com.study.basic

import org.apache.spark.mllib.linalg
import org.apache.spark.mllib.linalg.{Matrices, Matrix, Vectors}
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.stat.Statistics
import org.apache.spark.sql.SparkSession

/**
 * 假设检验
 * hypothesis testing
 * 拟合度检验要求输入为Vector: 验证一组观察值的次数分配是否异于理论上的分配。
 * 独立性检验要求输入是Matrix：用来检验两个属性间是否独立。
 *
 * @author stephen
 * @date 2019-08-27 11:27
 */
object HypothesisTestingDemo {

  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()

    spark.sparkContext.setLogLevel("warn")

    val path = this.getClass.getClassLoader.getResource("data/iris.data").getPath
    val rdd = spark.sparkContext.textFile(path)
    val data = rdd.map(_.split(",")).map(p => Vectors.dense(p(0).toDouble, p(1).toDouble, p(2).toDouble, p(3).toDouble))
    val v1: linalg.Vector = data.first
    val v2: linalg.Vector = data.take(2).last
    // 拟合度检验(适合度检验) Goodness fo fit
    val goodnessOfFitTestResult = Statistics.chiSqTest(v1)
    println(goodnessOfFitTestResult)
    // method: 方法。这里采用pearson方法。
    // statistic： 检验统计量。简单来说就是用来决定是否可以拒绝原假设的证据。
    //             检验统计量的值是利用样本数据计算得到的，它代表了样本中的信息。
    //             检验统计量的绝对值越大，拒绝原假设的理由越充分，反之，不拒绝原假设的理由越充分。
    // degrees of freedom：自由度。表示可自由变动的样本观测值的数目，
    // pValue：统计学根据显著性检验方法所得到的P 值。
    //         一般以P < 0.05 为显著， P<0.01 为非常显著，
    //         其含义是样本间的差异由抽样误差所致的概率小于0.05 或0.01。

    // 独立性检验
    // 检验是否独立的两个属性，一个是样本的序号，另一个是样本的数据值。
    val mat: Matrix = Matrices.dense(2, 2, Array(v1(0), v1(1), v2(0), v2(1)))
    val indenpendenceTestResult = Statistics.chiSqTest(mat)
    println(indenpendenceTestResult)
    //在本例中pValue =0.91，说明无法拒绝“样本序号与数据值无关”的假设。

    // 把v1作为样本，把v2作为期望值，进行卡方检验
    println(Statistics.chiSqTest(v1, v2))
    // 本例中pValue =0.998，说明样本v1与期望值等于V2的数据分布并无显著差异。

    // 键值对进行独立性检验
    val obs = rdd.map { line =>
      val parts = line.split(',')
      LabeledPoint(
        if (parts(4) == "Iris-setosa")
          0.toDouble
        else if (parts(4) == "Iris-versicolor")
          1.toDouble
        else
          2.toDouble,
        Vectors.dense(parts(0).toDouble, parts(1).toDouble,
          parts(2).toDouble, parts(3).toDouble))
    }
    // 返回一个包含每个特征对于标签的卡方检验的数组
    val featureTestResults = Statistics.chiSqTest(obs)
    var i = 1
    featureTestResults.foreach { result =>
      println(s"Column $i:\n$result")
      i += 1
    }
    // 这里实际上是把特征数据中的每一列都与标签进行独立性检验。
    // 可以看出，P值都非常小，说明可以拒绝“某列与标签列无关”的假设。
    // 也就是说，可以认为每一列的数据都与最后的标签有相关性。

    // 另一种检验方式：Kolmogorov-Smirnov 检验
    val test = rdd.map(_.split(",")).map(p => p(0).toDouble)
    val testResult = Statistics.kolmogorovSmirnovTest(test, "norm", 0, 1)
    println(testResult)

    spark.stop()
  }
}
